package wang.lxl.rbTree;

import java.util.Objects;

public class RBTree<T extends Comparable<T>> {
    private RBTNode<T> mRoot;    // 根结点

    private static final boolean RED   = false;
    private static final boolean BLACK = true;

    public class RBTNode<T extends Comparable<T>> {
        boolean color;        // 颜色
        T key;                // 关键字(键值)
        RBTNode<T> left;    // 左孩子
        RBTNode<T> right;    // 右孩子
        RBTNode<T> parent;    // 父结点

        public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
            this.key = key;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }

    }
    /*
     * 对红黑树的节点(x)进行左旋转
     *
     * 左旋示意图(对节点x进行左旋)：
     *      px                              px
     *     /                               /
     *    x                               y
     *   /  \      --(左旋)-.           / \                #
     *  lx   y                          x  ry
     *     /   \                       /  \
     *    ly   ry                     lx  ly
     *
     *
     */
    private void leftRotate(RBTNode<T> x) {
        // 设置x的右孩子为y
        RBTNode<T> y = x.right;
        // 将 "y的左孩子" 设为 "x的右孩子"
        x.right = y.left;
        // 如果y的左孩子非空,将 "x" 设为 "y的左孩子的父亲"
        if (Objects.nonNull(y.left)) {
            y.left.parent = x;
        }
        // 将 "x的父亲" 赋值给 "y的父亲"
        y.parent = x.parent;
        if (Objects.isNull(x.parent)) {
            // 如果 "x的父亲" 为空, 则将y设为根节点
            this.mRoot = y;
        } else {
            if (x.parent.left == x) {
                // 如果 x是它父节点的左孩子,则将y设为 "x的父节点的左孩子"
                x.parent.left = y;
            } else {
                // 将 y赋值给 "x的父节点的右孩子"
                x.parent.right = y;
            }
        }
        // 将 "x" 设为 "y的左孩子"
        y.left = x;
        // 将 "x的父节点" 设为 "y"
        x.parent = y;
    }
    /*
     * 对红黑树的节点(y)进行右旋转
     *
     * 右旋示意图(对节点y进行左旋)：
     *            py                               py
     *           /                                /
     *          y                                x
     *         /  \      --(右旋)-.            /  \                     #
     *        x   ry                           lx   y
     *       / \                                   / \                   #
     *      lx  rx                                rx  ry
     *
     */
    private void rightRotate(RBTNode<T> y) {
        // 设置x是当前节点的左孩子
        RBTNode<T> x = y.left;
        // 将 "x的右孩子: 赋值给 "y的左孩子"
        y.left = x.right;
        // 如果 "x的右孩子" 不为空,将 "y" 赋值给 "x的右孩子的父亲"
        if (Objects.nonNull(x.right)) {
            x.right.parent = y;
        }
        // 将 "y的父节点" 赋值给 "x的父节点"
        x.parent = y.parent;
        if (Objects.isNull(y.parent)) {
            // 如果 "y的父节点" 为空, 则将 x设为根节点
            this.mRoot = x;
        } else {
            if (y == y.parent.right) {
                // 如果 y是它父节点的右孩子, 则将 "x" 赋值给 "y父节点的右孩子"
                y.parent.right = x;
            } else {
                // 如果 y 是它父节点的左孩子,将 "x" 赋值给 "y的父节点的左孩子"
                y.parent.left = x;
            }
        }
        // 将 "y" 赋值给 "x的右孩子"
        x.right = y;
        // 将 "y的父节点" 设为 "x"
        y.parent = x;
    }

    /**
     * 插入节点
     * @param node
     */
    private void insert(RBTNode<T> node) {
        int cmp;
        RBTNode<T> y = null;
        RBTNode<T> x = this.mRoot;
        // 1,将红黑树当做一颗二叉查找树,将节点添加到二叉查找树中.
        while (Objects.nonNull(x)) {
            y = x;
            cmp = node.key.compareTo(x.key);
            if (cmp < 0) {
                x = x.left;
            } else {
                x = x.right;
            }
        }
        node.parent = y;
        if (Objects.nonNull(y)) {
            cmp = node.key.compareTo(y.key);
            if (cmp < 0) {
                y.left = node;
            } else {
                y.right = node;
            }
        } else {
            this.mRoot = node;
        } 
        // 2,设置节点的颜色为红色
        node.color = RED;
        // 3, 将它重新修正为一颗二叉查找树
        insertFixUp(node);
    }

    /**
     * 添加数据到树中
     * @param key
     */
    public void insert(T key) {
        RBTNode<T> node = new RBTNode<>(key, BLACK, null, null, null);
        if (Objects.nonNull(node)) {
            insert(node);
        }

    }
    /*
     * 红黑树插入修正函数
     *
     * 在向红黑树中插入节点之后(失去平衡)，再调用该函数；
     * 目的是将它重新塑造成一颗红黑树。
     *
     * 参数说明：
     *     node 插入的结点        // 对应《算法导论》中的z
     */
    private void insertFixUp(RBTNode<T> node) {
        RBTNode<T> parent,gparent;
        // 若父节点存在,并且父节点的颜色是红色
        while (Objects.nonNull(parent = parentOf(node)) && isRed(parent)) {
            gparent = parentOf(parent);
            // 若 "父节点" 是 "祖父节点的左孩子"
            if (parent == gparent.left) {
                // case1条件 : 叔叔节点是红色
                RBTNode<T> uncle = gparent.right;
                if (Objects.nonNull(uncle) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    node = gparent;
                    continue;
                }

                // case2条件: 叔叔是黑色,且当前节点是右孩子
                if (parent.right == node) {
                    RBTNode<T> tmp;
                    leftRotate(parent);
                    tmp = parent;
                    parent = node;
                    node = tmp;
                }
                // case3条件: 叔叔是黑色,且当前节点是左孩子
                setBlack(parent);
                setRed(gparent);
                rightRotate(gparent);
            } else {
                // 若父节点是 祖父节点的右孩子
                RBTNode<T> uncle = gparent.left;
                // case1条件: 叔叔节点是红色
                if (Objects.nonNull(uncle) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    node = gparent;
                    continue;
                }
                // case 2条件: 叔叔是黑色,且当前节点是左孩子
                if (parent.left == node) {
                    RBTNode<T> tmp;
                    rightRotate(parent);
                    tmp = parent;
                    parent = node;
                    node = tmp;
                }
                // case 3条件: 叔叔是黑色,且当前节点是右孩子
                setBlack(parent);
                setRed(gparent);
                leftRotate(gparent);
            }
        }
        // 设根节点为黑色
        setBlack(this.mRoot);
    }

    private void setRed(RBTNode<T> node) {
        node.color = RED;
    }

    private void setBlack(RBTNode<T> node) {
        node.color = BLACK;
    }

    private boolean isRed(RBTNode<T> node) {
        return node.color == RED;
    }

    private RBTNode<T> parentOf(RBTNode<T> node) {
        return node.parent;
    }

}
